#!/usr/bin/env python
u"""
HDF5_cryosat_L2.py (11/2019)
Reads and Writes HDF5 files for CryoSat-2 Level-2 data products
Supported CryoSat Modes: LRM, SAR, SARin, FDM, SID, GDR

OUTPUTS a formatted HDF5 file with:
    Data_1Hz: Time and Orbit Parameters
    Corrections: Elevation Corrections and Flags
    Data_20Hz: Geolocation and Elevation Measurements with Quality Parameters
    METADATA: MPH, SPH and DSD Header data

OPTIONS:
    BASELINE (HDF5_cryosat_L2): CryoSat-2 baseline (A, B, C)
    FILENAME (HDF5_cryosat_L2): output HDF5 file name
    TITLE (HDF5_cryosat_L2): output file description
    HEADER (HDF5_cryosat_L2): output CryoSat-2 file headers (MPH, SPH, DSD)
        1: for single CryoSat-2 files
        2: for merged CryoSat-2 files from convert_cryosat_L2.py
    CLOBBER (HDF5_cryosat_L2): overwrite existing HDF5 file
    VERBOSE: print HDF5 structure parameters to screen
    ATTRIBUTES (read_HDF5_cryosat_L2): input variable attributes from HDF5 file

PYTHON DEPENDENCIES:
    numpy: Scientific Computing Tools For Python (https://numpy.org)
    h5py: Python interface for Hierarchal Data Format 5 (HDF5)
        (https://www.h5py.org/)

UPDATE HISTORY:
    Updated 02/2020: convert from hard to soft tabulation
    Updated 11/2019: add netCDF4 Time (TAI seconds since 2000-01-01)
    Updated 10/2019: changing Y/N flags to True/False
    Updated 09/2019: updates for Baseline D
    Updated 04/2019: print HDF5 keys from list for python3 compatibility
    Updated 06/2018: use items instead of iteritems for python3 compatibility
    Updated 11/2016: added Abs_Orbit and Ascending_flag to Data_1Hz outputs
    Updated 05/2016: using __future__ print function
    Written 03/2016
"""
from __future__ import print_function

import os
import re
import h5py

#-- PURPOSE: write CryoSat-2 HDF5 files
def HDF5_cryosat_L2(CS_l2_mds, BASELINE, FILENAME='', TITLE='', HEADER=0,
    CLOBBER=True, VERBOSE=False):
    #-- setting HDF5 clobber attribute
    if CLOBBER:
        clobber = 'w'
    else:
        clobber = 'w-'

    #-- getting HDF5 dataset attributes for each variable
    CS_l2_attrib = cryosat_L2_attributes(BASELINE)

    #-- open output HDF5 file
    fileID = h5py.File(os.path.expanduser(FILENAME), clobber)
    #-- create sub-groups within HDF5 file
    fileID.create_group('Data_1Hz')
    fileID.create_group('Data_20Hz')
    fileID.create_group('Corrections')

    #-- Dimensions of parameters
    n_records,n_blocks = CS_l2_mds['Data_20Hz']['D_time_mics'].shape

    #-- find keys to output (do not output empty Spares variables)
    Data_1Hz_keys = [key for key in CS_l2_mds['Data_1Hz'].keys() if not
        re.search('Spare',key)]
    Data_20Hz_keys = [key for key in CS_l2_mds['Data_20Hz'].keys() if not
        re.search('Spare',key)]
    correction_keys = [key for key in CS_l2_mds['Corrections'].keys() if not
        re.search('Spare',key)]

    #-- create HDF5 records
    h5 = {}
    h5['Data_1Hz'] = {}
    h5['Data_20Hz'] = {}
    h5['Corrections'] = {}
    #-- CryoSat-2 1 Hz data fields (Location Group)
    #-- Time and Orbit Parameters plus Measurement Mode
    for key in Data_1Hz_keys:
        val = CS_l2_mds['Data_1Hz'][key]
        #-- Defining the HDF5 dataset variables
        h5['Data_1Hz'][key] = fileID.create_dataset('Data_1Hz/{0}'.format(key),
            (n_records,), data=val, dtype=val.dtype, compression='gzip')
        #-- add HDF5 variable attributes
        for att_name,att_val in CS_l2_attrib['Data_1Hz'][key].items():
            h5['Data_1Hz'][key].attrs[att_name] = att_val
        #-- attach dimensions
        h5['Data_1Hz'][key].dims[0].label='CS_L2_MDS_REC_SIZE'
    #-- CryoSat-2 20 Hz data fields (Measurement Group)
    #-- Derived from instrument measurement parameters
    for key in Data_20Hz_keys:
        val = CS_l2_mds['Data_20Hz'][key]
        #-- Defining the HDF5 dataset variables
        h5['Data_20Hz'][key] = fileID.create_dataset('Data_20Hz/{0}'.format(key),
            (n_records,n_blocks,), data=val, dtype=val.dtype, compression='gzip')
        #-- add HDF5 variable attributes
        for att_name,att_val in CS_l2_attrib['Data_20Hz'][key].items():
            h5['Data_20Hz'][key].attrs[att_name] = att_val
        #-- attach dimensions
        h5['Data_20Hz'][key].dims[0].label='CS_L2_MDS_REC_SIZE'
        h5['Data_20Hz'][key].dims[1].label='CS_L2_MDS_BLOCK_SIZE'
    #-- CryoSat-2 geophysical corrections (External Corrections Group)
    for key in correction_keys:
        val = CS_l2_mds['Corrections'][key]
        #-- Defining the HDF5 dataset variables
        h5['Corrections'][key] = fileID.create_dataset('Corrections/{0}'.format(key),
            (n_records,), data=val, dtype=val.dtype, compression='gzip')
        #-- add HDF5 variable attributes
        for att_name,att_val in CS_l2_attrib['Corrections'][key].items():
            h5['Corrections'][key].attrs[att_name] = att_val
        #-- attach dimensions
        h5['Corrections'][key].dims[0].label='CS_L2_MDS_REC_SIZE'

    #-- output MPH/SPH/DSD headers as group attributes
    if (HEADER == 1):
        #-- HEADER 1 is for single CryoSat-2 files
        fileID.create_group('METADATA')
        fileID['METADATA'].create_group('MPH')
        fileID['METADATA'].create_group('SPH')
        fileID['METADATA'].create_group('DSD')
        #-- Main Product Header (MPH) are all strings
        for att_name,att_val in CS_l2_mds['METADATA']['MPH'].items():
            fileID['METADATA']['MPH'].attrs[att_name] = att_val
        #-- Specific Product Header (SPH) are both strings and dictionaries
        for att_name,att_val in CS_l2_mds['METADATA']['SPH'].items():
            if isinstance(att_val,dict):
                #-- if att_val is dictionary
                fileID['METADATA']['SPH'].create_group(att_name)
                for ds_name,ds_val in att_val.items():
                    fileID['METADATA']['SPH'][att_name].attrs[ds_name] = ds_val
            elif isinstance(att_val,basestring) and att_name:
                #-- if att_val is string
                fileID['METADATA']['SPH'].attrs[att_name] = att_val
        #-- Data Set Descriptors (DSD) are all strings
        for att_name,att_val in CS_l2_mds['METADATA']['DSD'].items():
            fileID['METADATA']['DSD'].attrs[att_name] = att_val
    elif (HEADER == 2):
        #-- HEADER 2 is for merged CryoSat-2 files from convert_cryosat_L2.py
        fileID.create_group('METADATA')
        fileID['METADATA'].create_group('MPH')
        fileID['METADATA'].create_group('SPH')
        fileID['METADATA'].create_group('DSD')
        #-- Main Product Header (MPH) are all strings
        for fi in CS_l2_mds['METADATA']['MPH'].keys():
            fileID['METADATA']['MPH'].create_group(fi)
            for att_name,att_val in CS_l2_mds['METADATA']['MPH'][fi].items():
                fileID['METADATA']['MPH'][fi].attrs[att_name] = att_val
        #-- Specific Product Header (SPH) are both strings and dictionaries
        for fi in CS_l2_mds['METADATA']['SPH'].keys():
            fileID['METADATA']['SPH'].create_group(fi)
            for att_name,att_val in CS_l2_mds['METADATA']['SPH'][fi].items():
                if isinstance(att_val,dict):
                    #-- if att_val is dictionary
                    fileID['METADATA']['SPH'][fi].create_group(att_name)
                    for dsn,dsv in att_val.items():
                        fileID['METADATA']['SPH'][fi][att_name].attrs[dsn] = dsv
                elif isinstance(att_val,basestring) and att_name:
                    #-- if att_val is string
                    fileID['METADATA']['SPH'][fi].attrs[att_name] = att_val
        #-- Data Set Descriptors (DSD) are all strings
        for fi in CS_l2_mds['METADATA']['DSD'].keys():
            fileID['METADATA']['DSD'].create_group(fi)
            for att_name,att_val in CS_l2_mds['METADATA']['DSD'][fi].items():
                fileID['METADATA']['DSD'][fi].attrs[att_name] = att_val

    #-- output file title
    fileID.attrs['description'] = TITLE

    #-- Output HDF5 structure information
    if VERBOSE:
        print(FILENAME)
        print(list(fileID.keys()))

    #-- Closing the HDF5 file
    fileID.close()

#-- PURPOSE: read CryoSat-2 HDF5 files
def read_HDF5_cryosat_L2(FILENAME, ATTRIBUTES=True, VERBOSE=False):
    #-- Open the HDF5 file for reading
    fileID = h5py.File(os.path.expanduser(FILENAME), 'r')

    #-- Output HDF5 file information
    if VERBOSE:
        print(fileID.filename)
        print(list(fileID.keys()))

    #-- allocate python dictionaries for output CS_l2_mds variables
    CS_l2_mds = {}
    CS_l2_mds['Data_1Hz'] = {}
    CS_l2_mds['Data_20Hz'] = {}
    CS_l2_mds['Corrections'] = {}
    #-- get each HDF5 variable
    #-- CryoSat-2 Location Group
    for key in fileID['Data_1Hz'].keys():
        CS_l2_mds['Data_1Hz'][key] = fileID['Data_1Hz'][key][:]
    #-- CryoSat-2 Measurement Group
    for key in fileID['Data_20Hz'].keys():
        CS_l2_mds['Data_20Hz'][key] = fileID['Data_20Hz'][key][:,:]
    #-- CryoSat-2 External Corrections Group
    for key in fileID['Corrections'].keys():
        CS_l2_mds['Corrections'][key] = fileID['Corrections'][key][:]

    #-- Getting attributes of included variables
    if ATTRIBUTES:
        #-- allocate python dictionaries for output CS_l2_mds attributes
        CS_l2_mds['Attributes'] = {}
        CS_l2_mds['Attributes']['Data_1Hz'] = {}
        CS_l2_mds['Attributes']['Data_20Hz'] = {}
        CS_l2_mds['Attributes']['Corrections'] = {}
        #-- CryoSat-2 Location Group
        for key in fileID['Data_1Hz'].keys():
            CS_l2_mds['Attributes']['Data_1Hz'][key] = {}
            for att_name,att_val in fileID['Data_1Hz'][key].attrs.items():
                CS_l2_mds['Attributes']['Data_1Hz'][key][att_name] = att_val
        #-- CryoSat-2 Measurement Group
        for key in fileID['Data_20Hz'].keys():
            CS_l2_mds['Attributes']['Data_20Hz'][key] = {}
            for att_name,att_val in fileID['Data_20Hz'][key].attrs.items():
                CS_l2_mds['Attributes']['Data_20Hz'][key][att_name] = att_val
        #-- CryoSat-2 External Corrections Group
        for key in fileID['Corrections'].keys():
            CS_l2_mds['Attributes']['Corrections'][key] = {}
            for att_name,att_val in fileID['Corrections'][key].attrs.items():
                CS_l2_mds['Attributes']['Corrections'][key][att_name] = att_val
        #-- Global attribute description
        CS_l2_mds['Attributes']['title'] = fileID.attrs['description']

    #-- Closing the HDF5 file
    fileID.close()
    return CS_l2_mds

#-- PURPOSE: get the number of records and number of blocks in an HDF5 file
def HDF5_cryosat_L2_shape(FILENAME):
    #-- Open the HDF5 file for reading
    fileID = h5py.File(os.path.expanduser(FILENAME), 'r')
    n_records,n_blocks = fileID['Data_20Hz']['D_time_mics'].shape
    #-- Closing the HDF5 file
    fileID.close()
    return (n_records,n_blocks)

#-- PURPOSE: get attribute names for baseline
def cryosat_L2_attributes(BASELINE):
    #-- CryoSat-2 1 Hz data fields (Location Group)
    #-- Time and Orbit Parameters plus Measurement Mode
    L2_1Hz_attributes = {}
    #-- Time: netCDF4 TAI time since 2000-01-01 00:00:00
    L2_1Hz_attributes['Time'] = {}
    L2_1Hz_attributes['Time']['long_name'] = 'MDSR time stamp'
    L2_1Hz_attributes['Time']['units'] = 'seconds since 2000-01-01 00:00:00.0'
    L2_1Hz_attributes['Time']['description'] = ('TAI time counted in seconds '
        'since 2000-01-01 00:00:00')
    L2_1Hz_attributes['Time']['hertz'] = 1
    L2_1Hz_attributes['Time']['calendar'] = 'gregorian'
    L2_1Hz_attributes['Time']['standard_name'] = 'time'
    #-- Time: day part
    L2_1Hz_attributes['Day'] = {}
    L2_1Hz_attributes['Day']['long_name'] = 'MDSR time stamp days'
    L2_1Hz_attributes['Day']['units'] = 'days since 2000-01-01 00:00:00 TAI'
    L2_1Hz_attributes['Day']['hertz'] = 1
    #-- Time: second part
    L2_1Hz_attributes['Second'] = {}
    L2_1Hz_attributes['Second']['long_name'] = 'MDSR time stamp seconds'
    L2_1Hz_attributes['Second']['units'] = 'seconds'
    L2_1Hz_attributes['Second']['hertz'] = 1
    #-- Time: microsecond part
    L2_1Hz_attributes['Micsec'] = {}
    L2_1Hz_attributes['Micsec']['long_name'] = 'MDSR time stamp microseconds'
    L2_1Hz_attributes['Micsec']['units'] = 'microseconds'
    L2_1Hz_attributes['Micsec']['hertz'] = 1
    #-- SIRAL mode
    L2_1Hz_attributes['Siral_mode'] = {}
    L2_1Hz_attributes['Siral_mode']['long_name'] = 'SIRAL mode'
    L2_1Hz_attributes['Siral_mode']['hertz'] = 1
    #-- Lat_1Hz: packed units (0.1 micro-degree, 1e-7 degrees)
    L2_1Hz_attributes['Lat_1Hz'] = {}
    L2_1Hz_attributes['Lat_1Hz']['long_name'] = 'Latitude of measurement'
    L2_1Hz_attributes['Lat_1Hz']['description'] = ('Corresponding to the nadir '
        'position at the time of the 1Hz time stamp')
    L2_1Hz_attributes['Lat_1Hz']['units'] = '0.1 micro-degree'
    L2_1Hz_attributes['Lat_1Hz']['valid_min'] = -9e8
    L2_1Hz_attributes['Lat_1Hz']['valid_max'] = 9e8
    L2_1Hz_attributes['Lat_1Hz']['hertz'] = 1
    #-- Lon_1Hz: packed units (0.1 micro-degree, 1e-7 degrees)
    L2_1Hz_attributes['Lon_1Hz'] = {}
    L2_1Hz_attributes['Lon_1Hz']['long_name'] = 'Longitude of measurement'
    L2_1Hz_attributes['Lon_1Hz']['description'] = ('Corresponding to the nadir '
        'position at the time of the 1Hz time stamp')
    L2_1Hz_attributes['Lon_1Hz']['units'] = '0.1 micro-degree'
    L2_1Hz_attributes['Lon_1Hz']['valid_min'] = -18e8
    L2_1Hz_attributes['Lon_1Hz']['valid_max'] = 18e8
    L2_1Hz_attributes['Lon_1Hz']['hertz'] = 1
    #-- Alt_1Hz: packed units (mm, 1e-3 m)
    #-- Altitude of COG above reference ellipsoid (interpolated value)
    L2_1Hz_attributes['Alt_1Hz'] = {}
    L2_1Hz_attributes['Alt_1Hz']['long_name'] = 'Altitude'
    L2_1Hz_attributes['Alt_1Hz']['description'] = ('Altitude of Satellite COG '
        'above reference ellipsoid at nadir (interpolated value)')
    L2_1Hz_attributes['Alt_1Hz']['units'] = 'millimeters'
    L2_1Hz_attributes['Alt_1Hz']['hertz'] = 1

    #-- Spacecraft mispointing (Roll, Pitch, and Yaw for Baseline-C and D)
    if BASELINE in ('C','D'):
        #-- Roll: packed units (0.1 micro-degree, 1e-7 degrees)
        L2_1Hz_attributes['Roll'] = {}
        L2_1Hz_attributes['Roll']['long_name'] = ('Spacecraft roll angle '
            'derived from star trackers')
        L2_1Hz_attributes['Roll']['units'] = '0.1 micro-degree'
        L2_1Hz_attributes['Roll']['hertz'] = 1
        #-- Pitch: packed units (0.1 micro-degree, 1e-7 degrees)
        L2_1Hz_attributes['Pitch'] = {}
        L2_1Hz_attributes['Pitch']['long_name'] = ('Spacecraft pitch angle '
            'derived from star trackers')
        L2_1Hz_attributes['Pitch']['units'] = '0.1 micro-degree'
        L2_1Hz_attributes['Pitch']['hertz'] = 1
        #-- Yaw: packed units (0.1 micro-degree, 1e-7 degrees)
        L2_1Hz_attributes['Yaw'] = {}
        L2_1Hz_attributes['Yaw']['long_name'] = ('Spacecraft yaw angle '
            'derived from star trackers')
        L2_1Hz_attributes['Yaw']['units'] = '0.1 micro-degree'
        L2_1Hz_attributes['Yaw']['hertz'] = 1
    else:
        #-- Mispointing: packed units (millidegrees, 1e-3 degrees)
        L2_1Hz_attributes['Mispointing'] = {}
        L2_1Hz_attributes['Mispointing']['long_name'] =('Spacecraft mispointing'
            'derived from star trackers')
        L2_1Hz_attributes['Mispointing']['units'] = 'millidegrees'
        L2_1Hz_attributes['Mispointing']['hertz'] = 1

    #-- Number of valid records in the block of twenty that contain data
    #-- Last few records of the last block of a datset may be blank blocks
    #-- inserted to bring the file up to a multiple of twenty.
    L2_1Hz_attributes['N_valid'] = {}
    L2_1Hz_attributes['N_valid']['long_name'] = 'Number of valid measurements'
    L2_1Hz_attributes['N_valid']['description'] = ('The number of records in '
        'the block of twenty that actually contain data. The last few records '
        'of the last block of a datset may be blank blocks inserted to bring '
        'the file up to a multiple of twenty.')
    L2_1Hz_attributes['N_valid']['hertz'] = 1

    #-- Absolute Orbit Number from MPH Header
    L2_1Hz_attributes['Abs_Orbit'] = {}
    L2_1Hz_attributes['Abs_Orbit']['long_name'] = 'Absolute Orbit Number'
    L2_1Hz_attributes['Abs_Orbit']['units'] = 'Count'
    L2_1Hz_attributes['Abs_Orbit']['hertz'] = 1
    #-- Ascending Flag from SPH Header
    L2_1Hz_attributes['Ascending_flag'] = {}
    L2_1Hz_attributes['Ascending_flag']['long_name'] = 'Ascending Flag'
    L2_1Hz_attributes['Ascending_flag']['description'] = ('If True: satellite '
        'is in ascending orbit. If False: satellite is in descending orbit.')
    L2_1Hz_attributes['Ascending_flag']['hertz'] = 1

    #-- CryoSat-2 geophysical corrections (External Corrections Group)
    L2_corr_attributes = {}
    #-- Dry Tropospheric Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['dryTrop'] = {}
    L2_corr_attributes['dryTrop']['long_name'] = 'Dry Tropospheric Correction'
    L2_corr_attributes['dryTrop']['description'] = ('Altimeter range correction'
        ' due to the dry-gas component of the troposphere')
    L2_corr_attributes['dryTrop']['units'] = 'millimeters'
    L2_corr_attributes['dryTrop']['hertz'] = 1
    #-- Wet Tropospheric Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['wetTrop'] = {}
    L2_corr_attributes['wetTrop']['long_name'] = 'Wet Tropospheric Correction'
    L2_corr_attributes['wetTrop']['description'] = ('Altimeter range correction'
        ' due to the water component of the troposphere')
    L2_corr_attributes['wetTrop']['units'] = 'millimeters'
    L2_corr_attributes['wetTrop']['hertz'] = 1
    #-- Inverse Barometric Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['InvBar'] = {}
    L2_corr_attributes['InvBar']['long_name'] = 'Inverse Barometric Correction'
    L2_corr_attributes['InvBar']['description'] = ('Altimeter range correction '
        'for the depression of the ocean surface caused by the local barometric '
        'pressure')
    L2_corr_attributes['InvBar']['units'] = 'millimeters'
    L2_corr_attributes['InvBar']['hertz'] = 1
    #-- Dynamic Atmosphere Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['DAC'] = {}
    L2_corr_attributes['DAC']['long_name'] = 'Dynamic Atmosphere Correction'
    L2_corr_attributes['DAC']['description'] = ('Altimeter range correction '
        'for the dynamic component of the wind effect on the ocean '
        '(applicable only on ocean surface without sea-ice)')
    L2_corr_attributes['DAC']['units'] = 'millimeters'
    L2_corr_attributes['DAC']['hertz'] = 1
    #-- Ionospheric Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['Iono'] = {}
    L2_corr_attributes['Iono']['long_name'] = 'Ionospheric Correction'
    L2_corr_attributes['Iono']['description'] = ('Altimeter range correction '
        'for the delay of the radar pulse caused by free electrons in the '
        'ionosphere. Computed from a simple model in NRT data.')
    L2_corr_attributes['Iono']['units'] = 'millimeters'
    L2_corr_attributes['Iono']['hertz'] = 1
    #-- GIM Ionospheric Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['Iono_GIM'] = {}
    L2_corr_attributes['Iono_GIM']['long_name'] = 'GIM Ionospheric Correction'
    L2_corr_attributes['Iono_GIM']['description'] = ('Altimeter range correction '
        'for the delay of the radar pulse caused by free electrons in the '
        'ionosphere. Computed from GPS satellite derived (GIM) map. '
        'This correction is an alternative and only one should be used')
    L2_corr_attributes['Iono_GIM']['units'] = 'millimeters'
    L2_corr_attributes['Iono_GIM']['hertz'] = 1
    #-- Sea State Bias Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['SSB'] = {}
    L2_corr_attributes['SSB']['long_name'] = 'Sea State Bias Correction'
    L2_corr_attributes['SSB']['description'] = ('Empirical altimeter range '
        'correction proportional to the significant wave height which '
        'compensates for the asymmetric shape of ocean waves. '
        '(computed by the geophysical CFI library)')
    L2_corr_attributes['SSB']['units'] = 'millimeters'
    L2_corr_attributes['SSB']['hertz'] = 1
    #-- Ocean tide Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['ocTideElv'] = {}
    L2_corr_attributes['ocTideElv']['long_name'] = 'Ocean Tide'
    L2_corr_attributes['ocTideElv']['description'] = ('Removes the effect of '
        'local tide and adjusts the measurement to the mean sea surface')
    L2_corr_attributes['ocTideElv']['units'] = 'millimeters'
    L2_corr_attributes['ocTideElv']['hertz'] = 1
    #-- Long period equilibrium ocean tide Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['lpeTideElv'] = {}
    L2_corr_attributes['lpeTideElv']['long_name'] = ('Long-Period Equilibrium '
        'Ocean Tide')
    L2_corr_attributes['lpeTideElv']['description'] = ('Removes the effect of '
        'local tide and adjusts the measurement to the mean sea surface')
    L2_corr_attributes['lpeTideElv']['units'] = 'millimeters'
    L2_corr_attributes['lpeTideElv']['hertz'] = 1
    #-- Ocean loading tide Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['olTideElv'] = {}
    L2_corr_attributes['olTideElv']['long_name'] = 'Ocean Loading Tide'
    L2_corr_attributes['olTideElv']['description'] = ('Removes the effect of '
        'local tidal distortion of the Earth crust')
    L2_corr_attributes['olTideElv']['units'] = 'millimeters'
    L2_corr_attributes['olTideElv']['hertz'] = 1
    #-- Solid Earth tide Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['seTideElv'] = {}
    L2_corr_attributes['seTideElv']['long_name'] = 'Solid Earth Tide'
    L2_corr_attributes['seTideElv']['description'] = ('Removes the effect of '
        'local tidal distortion in the Earth crust')
    L2_corr_attributes['seTideElv']['units'] = 'millimeters'
    L2_corr_attributes['seTideElv']['hertz'] = 1
    #-- Geocentric Polar tide Correction packed units (mm, 1e-3 m)
    L2_corr_attributes['gpTideElv'] = {}
    L2_corr_attributes['gpTideElv']['long_name'] = 'Geocentric Polar Tide'
    L2_corr_attributes['gpTideElv']['description'] = ('Removes a long-period '
        'distortion of the Earth crust caused by variations in the centrifugal '
        'force as the Earth rotational axis moves its geographic location')
    L2_corr_attributes['gpTideElv']['units'] = 'millimeters'
    L2_corr_attributes['gpTideElv']['hertz'] = 1
    #-- Surface Type: Packed in groups of three bits for each of the 20 records
    L2_corr_attributes['Surf_type'] = {}
    L2_corr_attributes['Surf_type']['long_name'] = 'Surface Type Flag'
    L2_corr_attributes['Surf_type']['description'] = ('Enumerated key to '
        'classify surface at nadir provided by a model: (0=Open Ocean, '
        '1=Closed Sea, 2=Continental Ice, 3=Land, 4-7=currently unused)')
    L2_corr_attributes['Surf_type']['hertz'] = 1
    #-- Ocean Depth/Land Elevation Model (ODLE) packed units (mm, 1e-3 m)
    L2_corr_attributes['ODLE'] = {}
    L2_corr_attributes['ODLE']['long_name'] = 'ODLE from model'
    L2_corr_attributes['ODLE']['description'] = ('Ocean Depth / Land Elevation'
        'model supplied in the Geophysical Corrections CFI library')
    L2_corr_attributes['ODLE']['units'] = 'millimeters'
    L2_corr_attributes['ODLE']['hertz'] = 1
    #-- Ice Concentration packed units (%/100)
    L2_corr_attributes['Ice_conc'] = {}
    L2_corr_attributes['Ice_conc']['long_name'] = 'Ice Concentration'
    L2_corr_attributes['Ice_conc']['description'] = ('Obtained from a dynamic '
        'auxiliary file if data is available for the current time period. '
        'If data is not available, a climatology model is used.')
    L2_corr_attributes['Ice_conc']['units'] = '1/100 of a percent'
    L2_corr_attributes['Ice_conc']['hertz'] = 1
    #-- Snow Depth packed units (mm, 1e-3 m)
    L2_corr_attributes['Snow_depth'] = {}
    L2_corr_attributes['Snow_depth']['long_name'] = 'Snow Depth'
    L2_corr_attributes['Snow_depth']['description'] = ('Obtained from'
        'climatology model data. Can be used to adjust the freeboard estimate '
        'to account for snow-loading.')
    L2_corr_attributes['Snow_depth']['units'] = 'millimeters'
    L2_corr_attributes['Snow_depth']['hertz'] = 1
    #-- Snow Density packed units (kg/m^3)
    L2_corr_attributes['Snow_density'] = {}
    L2_corr_attributes['Snow_density']['long_name'] = 'Snow Density'
    L2_corr_attributes['Snow_density']['description'] = ('Obtained from'
        'climatology model data. Can be used to adjust the freeboard estimate '
        'to account for snow-loading.')
    L2_corr_attributes['Snow_density']['units'] = 'kg/m^3'
    L2_corr_attributes['Snow_density']['hertz'] = 1
    #-- Corrections Status Flag
    L2_corr_attributes['C_status'] = {}
    L2_corr_attributes['C_status']['long_name'] = 'Corrections Status Flag'
    L2_corr_attributes['C_status']['description'] = ('Used to show validity of '
        'the 1Hz corrections.  See table 2.3.3.1-4 of the "L2 Products Format '
        'Specification" document')
    L2_corr_attributes['C_status']['hertz'] = 1
    #-- Significant Wave Height (SWH) packed units (mm, 1e-3)
    L2_corr_attributes['SWH'] = {}
    L2_corr_attributes['SWH']['long_name'] = 'Significant Wave Height (SWH)'
    L2_corr_attributes['SWH']['units'] = 'millimeters'
    L2_corr_attributes['SWH']['hertz'] = 1
    #-- Wind Speed packed units (mm/s, 1e-3 m/s)
    L2_corr_attributes['Wind_speed'] = {}
    L2_corr_attributes['Wind_speed']['long_name'] = 'Altimetric wind speed'
    L2_corr_attributes['Wind_speed']['description'] = ('Calculated using a '
        'model by the geocorrections CFI')
    L2_corr_attributes['Wind_speed']['units'] = 'mm/second'
    L2_corr_attributes['Wind_speed']['hertz'] = 1
    #-- Geoid height and mean sea surface for Baseline D
    if (BASELINE == 'D'):
        #-- Geoid: packed units (mm, 1e-3 m)
        L2_corr_attributes['Geoid'] = {}
        L2_corr_attributes['Geoid']['long_name'] = 'Geoid height'
        L2_corr_attributes['Geoid']['description'] = ('Computed from the geoid '
            'model with a correction to refer the value to the mean tide '
            'system. Includes permanent tide (zero frequency).')
        L2_corr_attributes['Geoid']['standard_name'] = ('geoid_height_above_'
            'reference_ellipsoid')
        L2_corr_attributes['Geoid']['units'] = 'millimeters'
        L2_corr_attributes['Geoid']['source'] = "EGM96"
        L2_corr_attributes['Geoid']['institution'] = "NASA GSFC and NIMA"
        L2_corr_attributes['Geoid']['reference'] = ('Lemoine, F. G., S. C. '
            'Kenyon, J. K. Factor, R.G. Trimmer, N. K. Pavlis, D. S. Chinn, '
            'C. M. Cox, S. M. Klosko, S. B. Luthcke, M. H. Torrence, Y. M. '
            'Wang, R. G. Williamson, E. C. Pavlis, R. H. Rapp and T. R. Olson. '
            'The Development of the Joint NASA GSFC and the National Imagery '
            'and Mapping Agency (NIMA) Geopotential Model EGM96. '
            'NASA/TP-1998-206861, July 1998.')
        L2_corr_attributes['Geoid']['hertz'] = 1
        #-- Mean Sea Surface: packed units (mm, 1e-3 m)
        L2_corr_attributes['MSS'] = {}
        L2_corr_attributes['MSS']['long_name'] = 'Mean sea surface height'
        L2_corr_attributes['MSS']['description'] = ('Mean sea surface model, '
            'referenced to the WGS84 ellipsoid. This model has been optimised '
            'for use in computing the surface height anomaly of the polar '
            'oceans to derive sea-ice freeboard. The model is a merge of the '
            'CLS2011 mean sea-surface and CryoSat data from high latitudes.')
        L2_corr_attributes['MSS']['standard_name'] = ('sea_surface_height_'
            'above_reference_ellipsoid')
        L2_corr_attributes['MSS']['units'] = 'millimeters'
        L2_corr_attributes['MSS']['source'] = "UCL13"
        L2_corr_attributes['MSS']['institution'] = "UCL"
        L2_corr_attributes['MSS']['hertz'] = 1
    else:
        #-- Mean Sea Surface or Geoid packed units (mm, 1e-3 m)
        L2_corr_attributes['MSS_Geoid'] = {}
        L2_corr_attributes['MSS_Geoid']['long_name']='Mean Sea Surface or Geoid'
        L2_corr_attributes['MSS_Geoid']['description'] = ('Over Ocean (Surface'
            'Types 0 and 1): surface height from the Mean Sea Surface. Over '
            'Land Surface Types 2 and 3): geoid height from the CFI library.')
        L2_corr_attributes['MSS_Geoid']['units'] = 'millimeters'
        L2_corr_attributes['MSS_Geoid']['hertz'] = 1

    #-- CryoSat-2 20 Hz data fields (Measurement Group)
    #-- Derived from instrument measurement parameters
    L2_20Hz_attributes = {}
    #-- Time: netCDF4 TAI time since 2000-01-01 00:00:00
    L2_20Hz_attributes['Time'] = {}
    L2_20Hz_attributes['Time']['long_name'] = 'MDSR time stamp'
    L2_20Hz_attributes['Time']['units'] = 'seconds since 2000-01-01 00:00:00.0'
    L2_20Hz_attributes['Time']['description'] = ('TAI time counted in seconds '
        'since 2000-01-01 00:00:00')
    L2_20Hz_attributes['Time']['hertz'] = 1
    L2_20Hz_attributes['Time']['calendar'] = 'gregorian'
    L2_20Hz_attributes['Time']['standard_name'] = 'time'
    #-- Delta between the timestamps for 20Hz record and the 1Hz record
    #-- D_time_mics packed units (microseconds)
    L2_20Hz_attributes['D_time_mics'] = {}
    L2_20Hz_attributes['D_time_mics']['long_name'] = 'Delta Time'
    L2_20Hz_attributes['D_time_mics']['description'] = ('Adds to the 1 Hz time '
        'stamp to give the correct time for each of the 20Hz measurement '
        '(Set to zero if block is partially empty)')
    L2_20Hz_attributes['D_time_mics']['units'] = 'microseconds'
    L2_20Hz_attributes['D_time_mics']['hertz'] = 20
    #-- Lat: packed units (0.1 micro-degree, 1e-7 degrees)
    L2_20Hz_attributes['Lat'] = {}
    L2_20Hz_attributes['Lat']['long_name'] = 'Latitude of measurement'
    L2_20Hz_attributes['Lat']['description'] = ('Measurement Latitude of the '
        'echoing point position.  Includes the x-track offset for SIN '
        'measurements and the slope-corrected position for LRM measurements.')
    L2_20Hz_attributes['Lat']['units'] = '0.1 micro-degree'
    L2_20Hz_attributes['Lat']['valid_min'] = -9e8
    L2_20Hz_attributes['Lat']['valid_max'] = 9e8
    L2_20Hz_attributes['Lat']['hertz'] = 20
    #-- Lon: packed units (0.1 micro-degree, 1e-7 degrees)
    L2_20Hz_attributes['Lon'] = {}
    L2_20Hz_attributes['Lon']['long_name'] = 'Longitude of measurement'
    L2_20Hz_attributes['Lon']['description'] = ('Measurement Longitude of the '
        'echoing point position.  Includes the x-track offset for SIN '
        'measurements and the slope-corrected position for LRM measurements.')
    L2_20Hz_attributes['Lon']['units'] = '0.1 micro-degree'
    L2_20Hz_attributes['Lon']['valid_min'] = -18e8
    L2_20Hz_attributes['Lon']['valid_max'] = 18e8
    L2_20Hz_attributes['Lon']['hertz'] = 20

    #-- Measured elevation and Backscatter (for 3 retrackers with Baseline-C,D)
    if (BASELINE == 'C'):
        #-- Measured elevation above ellipsoid from retracker 1
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev_1'] = {}
        L2_20Hz_attributes['Elev_1']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev_1']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev_1']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev_1']['retracker'] = 1
        L2_20Hz_attributes['Elev_1']['hertz'] = 20
        #-- Measured elevation above ellipsoid from retracker 2
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev_2'] = {}
        L2_20Hz_attributes['Elev_2']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev_2']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev_2']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev_2']['retracker'] = 2
        L2_20Hz_attributes['Elev_2']['hertz'] = 20
        #-- Measured elevation above ellipsoid from retracker 3
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev_3'] = {}
        L2_20Hz_attributes['Elev_3']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev_3']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev_3']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev_3']['retracker'] = 3
        L2_20Hz_attributes['Elev_3']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker 1: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0_1'] = {}
        L2_20Hz_attributes['Sig0_1']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0_1']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0_1']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0_1']['retracker'] = 1
        L2_20Hz_attributes['Sig0_1']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker 2: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0_2'] = {}
        L2_20Hz_attributes['Sig0_2']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0_2']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0_2']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0_2']['retracker'] = 2
        L2_20Hz_attributes['Sig0_2']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker 3: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0_3'] = {}
        L2_20Hz_attributes['Sig0_3']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0_3']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0_3']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0_3']['retracker'] = 3
        L2_20Hz_attributes['Sig0_3']['hertz'] = 20
    elif (BASELINE == 'D'):
        #-- Measured elevation above ellipsoid from retracker 1
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev_1'] = {}
        L2_20Hz_attributes['Elev_1']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev_1']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev_1']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev_1']['retracker'] = 1
        L2_20Hz_attributes['Elev_1']['hertz'] = 20
        #-- Measured elevation above ellipsoid from retracker 2
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev_2'] = {}
        L2_20Hz_attributes['Elev_2']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev_2']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev_2']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev_2']['retracker'] = 2
        L2_20Hz_attributes['Elev_2']['hertz'] = 20
        #-- Measured elevation above ellipsoid from retracker 3
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev_3'] = {}
        L2_20Hz_attributes['Elev_3']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev_3']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev_3']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev_3']['retracker'] = 3
        L2_20Hz_attributes['Elev_3']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker 1: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0_1'] = {}
        L2_20Hz_attributes['Sig0_1']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0_1']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0_1']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0_1']['retracker'] = 1
        L2_20Hz_attributes['Sig0_1']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker 2: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0_2'] = {}
        L2_20Hz_attributes['Sig0_2']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0_2']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0_2']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0_2']['retracker'] = 2
        L2_20Hz_attributes['Sig0_2']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker 3: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0_3'] = {}
        L2_20Hz_attributes['Sig0_3']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0_3']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0_3']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0_3']['retracker'] = 3
        L2_20Hz_attributes['Sig0_3']['hertz'] = 20
        #-- Measured range from the satellite to the surface from retracker 1
        L2_20Hz_attributes['Range_1'] = {}
        L2_20Hz_attributes['Range_1']['long_name'] = 'Range to surface'
        L2_20Hz_attributes['Range_1']['description'] =('Measured range from '
            'the satellite to the surface at the coordinate location. All '
            'instrumental corrections applied.  Does not include geophysical '
            'corrections.')
        L2_20Hz_attributes['Range_1']['units'] = 'mm'
        L2_20Hz_attributes['Range_1']['retracker'] = 1
        L2_20Hz_attributes['Range_1']['hertz'] = 20
        #-- Measured range from the satellite to the surface from retracker 2
        L2_20Hz_attributes['Range_2'] = {}
        L2_20Hz_attributes['Range_2']['long_name'] = 'Range to surface'
        L2_20Hz_attributes['Range_2']['description'] =('Measured range from '
            'the satellite to the surface at the coordinate location. All '
            'instrumental corrections applied.  Does not include geophysical '
            'corrections.')
        L2_20Hz_attributes['Range_2']['units'] = 'mm'
        L2_20Hz_attributes['Range_2']['retracker'] = 2
        L2_20Hz_attributes['Range_2']['hertz'] = 20
        #-- Measured range from the satellite to the surface from retracker 3
        L2_20Hz_attributes['Range_3'] = {}
        L2_20Hz_attributes['Range_3']['long_name'] = 'Range to surface'
        L2_20Hz_attributes['Range_3']['description'] = ('Measured range from '
            'the satellite to the surface at the coordinate location. All '
            'instrumental corrections applied.  Does not include geophysical '
            'corrections.')
        L2_20Hz_attributes['Range_3']['units'] = 'mm'
        L2_20Hz_attributes['Range_3']['retracker'] = 3
        L2_20Hz_attributes['Range_3']['hertz'] = 20
        #-- Sea ice Floe height
        L2_20Hz_attributes['Sea_Ice_Floe'] = {}
        L2_20Hz_attributes['Sea_Ice_Floe']['long_name'] = 'Sea ice Floe height'
        L2_20Hz_attributes['Sea_Ice_Floe']['description'] = ('Measured height '
            'of the surface above the reference ellipsoid (WGS84) at the '
            'coordinate location. This retracker is run over all surfaces not '
            'discriminated as a sea-ice lead, not just over sea-ice floes.')
        L2_20Hz_attributes['Sea_Ice_Floe']['units'] = 'mm'
        L2_20Hz_attributes['Sea_Ice_Floe']['retracker'] = 3
        L2_20Hz_attributes['Sea_Ice_Floe']['hertz'] = 20
        #-- Sea ice lead height
        L2_20Hz_attributes['Sea_Ice_Lead'] = {}
        L2_20Hz_attributes['Sea_Ice_Lead']['long_name'] = 'Sea ice lead height'
        L2_20Hz_attributes['Sea_Ice_Lead']['description'] = ('Measured height '
            'of the surface above the reference ellipsoid (WGS84) at the '
            'coordinate location. ')
        L2_20Hz_attributes['Sea_Ice_Lead']['units'] = 'mm'
        L2_20Hz_attributes['Sea_Ice_Lead']['retracker'] = 3
        L2_20Hz_attributes['Sea_Ice_Lead']['hertz'] = 20
    else:
        #-- Measured elevation above ellipsoid from retracker
        #-- packed units (mm, 1e-3 m)
        L2_20Hz_attributes['Elev'] = {}
        L2_20Hz_attributes['Elev']['long_name'] = 'Surface Elevation'
        L2_20Hz_attributes['Elev']['description'] = ('Height of surface at '
            'measurement point with respect to the reference ellipsoid (WGS84). '
            'It is calculated with the retracked and geocorrected range')
        L2_20Hz_attributes['Elev']['units'] = 'millimeters'
        L2_20Hz_attributes['Elev']['hertz'] = 20
        #-- Sigma Zero Backscatter for retracker: packed units (1e-2 dB)
        L2_20Hz_attributes['Sig0'] = {}
        L2_20Hz_attributes['Sig0']['long_name'] = 'Sigma Zero Backscatter'
        L2_20Hz_attributes['Sig0']['description'] = ('Fully corrected '
            'including instrument gain corrections and retracker correction')
        L2_20Hz_attributes['Sig0']['units'] = 'dB/100'
        L2_20Hz_attributes['Sig0']['hertz'] = 20

    #-- Freeboard: packed units (mm, 1e-3 m)
    #-- -9999 default value indicates computation has not been performed
    L2_20Hz_attributes['Freeboard'] = {}
    L2_20Hz_attributes['Freeboard']['long_name'] = 'Freeboard'
    L2_20Hz_attributes['Freeboard']['description'] = ('SAR mode computed '
        'freeboard of the Sea Ice. Freeboard can be a small negative value '
        'when there is sufficient snow loading on thin ice. Set to 0 in SIN '
        'and LRM modes.')
    L2_20Hz_attributes['Freeboard']['units'] = 'millimeters'
    L2_20Hz_attributes['Freeboard']['_FillValue'] = -9999
    L2_20Hz_attributes['Freeboard']['hertz'] = 20
    #-- Interpolated Sea Surface Height Anomaly: packed units (mm, 1e-3 m)
    L2_20Hz_attributes['SSHA_interp'] = {}
    L2_20Hz_attributes['SSHA_interp']['long_name'] = ('Interpolated Sea '
        'Surface Height Anomaly')
    L2_20Hz_attributes['SSHA_interp']['description'] = ('Ocean height anomaly '
        'defined by comparing the interpolated ocean height from the SAR '
        'processing with the MSS from the model')
    L2_20Hz_attributes['SSHA_interp']['units'] = 'millimeters'
    L2_20Hz_attributes['SSHA_interp']['hertz'] = 20
    #-- Interpolated Sea Surface Height measurement count
    L2_20Hz_attributes['SSHA_interp_count'] = {}
    L2_20Hz_attributes['SSHA_interp_count']['long_name'] = ('Interpolated Sea '
        'Surface Height measurement count')
    L2_20Hz_attributes['SSHA_interp_count']['description'] = ('Number of records used '
        'to create the fit in the SSHA calculation')
    L2_20Hz_attributes['SSHA_interp_count']['hertz'] = 20
    #-- Interpolation quality estimate RSS: packed units (mm, 1e-3 m)
    L2_20Hz_attributes['SSHA_interp_RMS'] = {}
    L2_20Hz_attributes['SSHA_interp_RMS']['long_name'] = ('Interpolated Sea '
        'Surface Height quality estimate')
    L2_20Hz_attributes['SSHA_interp_RMS']['description'] = ('Root mean square (RMS)'
        'of the residuals of the SSHA fit')
    L2_20Hz_attributes['SSHA_interp_RMS']['units'] = 'millimeters'
    L2_20Hz_attributes['SSHA_interp_RMS']['hertz'] = 20
    #-- Peakiness: packed units (1e-2)
    L2_20Hz_attributes['Peakiness'] = {}
    L2_20Hz_attributes['Peakiness']['long_name'] = 'Peakiness'
    L2_20Hz_attributes['Peakiness']['description'] = ('Peakiness of the echo '
        'in the L1b product. Requires different interpretation for SAR and SIN '
        'echoes which do not have the usual pulse-limited echo shape.')
    L2_20Hz_attributes['Peakiness']['units'] = '1/100'
    L2_20Hz_attributes['Peakiness']['hertz'] = 20
    #-- Number of averaged echoes or beams
    L2_20Hz_attributes['N_avg'] = {}
    L2_20Hz_attributes['N_avg']['long_name'] = 'Number of Echoes/Beams averaged'
    L2_20Hz_attributes['N_avg']['description'] = ('In LRM mode: number of '
        'echoes which have been averaged to make one measurement (normally). '
        'In SAR and SIN modes: number of Doppler beams which have been stacked '
        'to derive each measurement. Near the begining and end of each section '
        'of SAR or SIN mode operation, this number reduces below the nominal '
        'value and there is a corresponding decrease in the signal to noise '
        'ratio of the waveform.')
    L2_20Hz_attributes['N_avg']['hertz'] = 20
    #-- Quality flags
    L2_20Hz_attributes['Quality_flag'] = {}
    L2_20Hz_attributes['Quality_flag']['long_name'] = 'Measurement Quality Flags'
    L2_20Hz_attributes['Quality_flag']['description'] = ('Used to show validity '
        'of the 20Hz measurements.  See table 2.3.3.1-5 of the "L2 Products '
        'Format Specification" document')
    L2_20Hz_attributes['Quality_flag']['hertz'] = 20

    #-- Corrections Flag and Quality metrics for 3 retrackers with Baseline-C,D
    if (BASELINE == 'C'):
        #-- Corrections Application Flag
        L2_20Hz_attributes['Corrections_flag'] = {}
        L2_20Hz_attributes['Corrections_flag']['long_name'] = ('Corrections '
            'Application Flag')
        L2_20Hz_attributes['Corrections_flag']['hertz'] = 20
        #-- Quality metric for retracker 1
        L2_20Hz_attributes['Quality_1'] = {}
        L2_20Hz_attributes['Quality_1']['long_name'] = ('Quality metric for '
            'retracker')
        L2_20Hz_attributes['Quality_1']['retracker'] = 1
        L2_20Hz_attributes['Quality_1']['hertz'] = 20
        #-- Quality metric for retracker 2
        L2_20Hz_attributes['Quality_2'] = {}
        L2_20Hz_attributes['Quality_2']['long_name'] = ('Quality metric for '
            'retracker')
        L2_20Hz_attributes['Quality_2']['retracker'] = 2
        L2_20Hz_attributes['Quality_2']['hertz'] = 20
        #-- Quality metric for retracker 3
        L2_20Hz_attributes['Quality_3'] = {}
        L2_20Hz_attributes['Quality_3']['long_name'] = ('Quality metric for '
            'retracker')
        L2_20Hz_attributes['Quality_3']['retracker'] = 3
        L2_20Hz_attributes['Quality_3']['hertz'] = 20
    elif (BASELINE == 'D'):
        #-- Corrections Application Flag
        L2_20Hz_attributes['Corrections_flag'] = {}
        L2_20Hz_attributes['Corrections_flag']['long_name'] = ('Corrections '
            'Application Flag')
        L2_20Hz_attributes['Corrections_flag']['hertz'] = 20
        #-- Measurement mode
        L2_20Hz_attributes['Measurement_Mode'] = {}
        L2_20Hz_attributes['Measurement_Mode']['long_name'] = ('Instrument '
            'measurement mode derived from configuration bits in L0')
        L2_20Hz_attributes['Measurement_Mode']['flag_values'] = [1,2,3]
        L2_20Hz_attributes['Measurement_Mode']['flag_meanings']='lrm sar sarin'
        L2_20Hz_attributes['Measurement_Mode']['hertz'] = 20
        #-- Surface Type
        L2_20Hz_attributes['Surf_type'] = {}
        L2_20Hz_attributes['Surf_type']['long_name'] = 'surface type from mask'
        L2_20Hz_attributes['Surf_type']['description'] = ('A 4-state surface '
            'type mask for Cryosat2 data for the surface type at the nadir '
            'location. Computed by combining data from different sources: '
            'GSHHG, GlobCover, MODIS Mosaic of Antarctica, and Water body '
            'outlines from LEGOS')
        L2_20Hz_attributes['Surf_type']['flag_values'] = [0,1,2,3]
        L2_20Hz_attributes['Surf_type']['flag_meanings'] = ('ocean '
            'lake_enclosed_sea ice land')
        L2_20Hz_attributes['Surf_type']['source'] = ('GSHHG, GlobCover, '
            'MODIS Mosaic of Antarctica, and Water body outlines from LEGOS')
        L2_20Hz_attributes['Surf_type']['hertz'] = 20
        #-- Quality metric for retracker 1
        L2_20Hz_attributes['Quality_1'] = {}
        L2_20Hz_attributes['Quality_1']['long_name'] = ('Quality metric for '
            'retracker')
        L2_20Hz_attributes['Quality_1']['retracker'] = 1
        L2_20Hz_attributes['Quality_1']['hertz'] = 20
        #-- Quality metric for retracker 2
        L2_20Hz_attributes['Quality_2'] = {}
        L2_20Hz_attributes['Quality_2']['long_name'] = ('Quality metric for '
            'retracker')
        L2_20Hz_attributes['Quality_2']['retracker'] = 2
        L2_20Hz_attributes['Quality_2']['hertz'] = 20
        #-- Quality metric for retracker 3
        L2_20Hz_attributes['Quality_3'] = {}
        L2_20Hz_attributes['Quality_3']['long_name'] = ('Quality metric for '
            'retracker')
        L2_20Hz_attributes['Quality_3']['retracker'] = 3
        L2_20Hz_attributes['Quality_3']['hertz'] = 20

    #-- Bind all the l2 attributes together into single dictionary
    CS_l2_attrib = {}
    CS_l2_attrib['Data_1Hz'] = L2_1Hz_attributes
    CS_l2_attrib['Corrections'] = L2_corr_attributes
    CS_l2_attrib['Data_20Hz'] = L2_20Hz_attributes
    #-- return the output dictionary
    return CS_l2_attrib
